
//
// 120228a- AH - Created.
// 120228b- AH - Changed return value from distance(String, String) from int
//               to float.
// 120228c- AH - Changed distance to metric.
// 120228 - AH - (Midtun) Changed from interface to abstract class, implemented
//               match(String, String) and matchIgnoreCase(String, String).
//

package org.soekrat.engine.util;

/**
 * <p>
 *   Interface for objects that computes distances between 
 *   {@code String}s. 
 * </p>
 * @author Arne Halvorsen (AH)
 */
public abstract class StringMetric 
{
  protected float m_threshold=0.3f;
  
  protected final int id; // debug stuff.
  
  private static int count=0; // debug stuff.
  
  protected StringMetric(){ id=count; count++; } 
  
  /**
   * <p>
   *   Gets threshold to use for {@code this} metric. 
   * </p>
   * @return Values a metric is less than if two
   *         {@code String}s consider similar using {@code this}.
   */
  public final float getThreshold(){ return m_threshold; }
  
  /**
   * <p>
   *   Sets threshold to use for {@code this} metric.
   * </p>
   * @param v Values a metric is less than if two
   *         {@code String}s consider similar using {@code this}. 
   */
  public final void setThreshold(float v)
  {
    if (v<0.0f)
    {
      throw new IllegalArgumentException("v<0.0 : "+v);
    }
    
    m_threshold=v;
  }
  
  /**
   * <p>
   *   Gets distance.
   * </p>
   * @param s0 First
   *           {@link String}.
   * @param s1 Second
   *           {@link String}.
   * @return Distance.
   */
  public abstract float metric(String s0, String s1);
  
  /**
   * <p>
   *   Matches.
   * </p>
   * @param s0 First
   *           {@link String}.
   * @param s1 Second
   *           {@link String}.
   * @return {@code metric(s0, s1)<threshold()}.
   */
  public final boolean match(String s0, String s1)
  {
    if (s0==null)
    {
      throw new NullPointerException("s0");
    }
    if (s1==null)
    {
      throw new NullPointerException("s1");
    }
    
    if (s0==s1) return true;
    
    if (s0.equals(s1)) return true;
    
    float metric=metric(s0, s1);
    
    if (metric<getThreshold()) return true;
    
    return false;
  }
  
  /**
   * <p>
   *   Matches ignoring case.
   * </p>
   * @param s0 First
   *           {@link String}.
   * @param s1 Second
   *           {@link String}.
   * @return {@code metric(s0.toLowerCase(), s1.toLowerCase())<threshold()}.
   */
  public final boolean matchIgnoreCase(String s0, String s1)
  {
    if (s0==null)
    {
      throw new NullPointerException("s0");
    }
    if (s1==null)
    {
      throw new NullPointerException("s1");
    }
    
    if (s0==s1) return true;
    
    s0=s0.toLowerCase();
    s1=s1.toLowerCase();
    
    if (s0.equals(s1)) return true;
    
    float metric=metric(s0, s1);
    
    //System.out.println("id: "+id+", matches : '"+s0+"' and '"+s1+"' metric: "+metric+
    //  " threshold: "+getThreshold());
    if (metric<getThreshold()) 
    {
      //System.out.println("  matched");
      return true;
    }
    
    //System.out.println("  matched NOT");
    return false;
  }
  
  @Override
  public String toString() 
  {
    StringBuilder sb=new StringBuilder();
    sb.append("id=").append(id).append(", threshold=").append(m_threshold);
    return sb.toString();
  }
  
}
